﻿/*****************************************************
作    者：郝伟老师
创建日期：2023/08/03
主要作用：软件中的参数的保存。
参考文档：http://121.199.10.158:8107/ebooks/ebook-windgoes/_book/utils/ConfigHelper.html
【更新历史】
2023/08/03 添加说明文档。
2023/08/01 新建类

******************************************************/
using System;
using System.IO;
using System.Text;
using System.Collections.Generic;


namespace WindGoes6.Utils
{
    /// <summary>
    /// 用于从字符中加载数据。
    /// </summary>
    public class ConfigHelper
    {
        /// <summary>
        /// 用于存储变量的值。
        /// </summary>
        Dictionary<string, string> dict = new Dictionary<string, string>();


        /// <summary>
        /// 配置文件路径，使用的格式为字典。
        /// </summary>
        public string ConfigFilepath { get; set; } = "config.dict";



        /// <summary>
        /// 返回所有的密钥，只读属性，每次返回新实例，修改不会影响原Keys。
        /// </summary>
        public List<string> Keys
        {
            get
            {
                List<string> keys = new List<string>();
                foreach (string key in dict.Keys)
                    keys.Add(key);
                return keys;
            }
        }

        /// <summary>
        /// 所有的值。
        /// </summary>
        public List<string> Values
        {
            get
            {
                List<string> values = new List<string>();
                foreach (string key in dict.Keys)
                    values.Add(dict[key]);
                return values;
            }
        }

        #region 构造函数
        /// <summary>
        /// 初始化空字典，不进行任何加载。
        /// </summary>
        public ConfigHelper() { }

        /// <summary>
        /// 使用指定文件初始化字典，如果失败则返回一个新建的字典对象。 
        /// </summary>
        /// <param name="dictfile">输入的配置文件路径。</param>
        /// 
        public ConfigHelper(string dictfile)
        {
            Load(ConfigFilepath);
        }
        #endregion

        #region 读取函数

        /// <summary>
        /// 如果字典dict中包括key，则返回 dict[key]，否则返回null
        /// </summary>
        /// <param name="key">访问的键。</param>
        /// <returns></returns>
        public string this[string key]
        {
            get => key != null && dict != null && dict.ContainsKey(key) ? dict[key] : null;
            set
            {
                // Dictionary 类可以直接使用 dict[key]=value 的方式添加，【无论key是否存在！】
                if (value != null)
                    dict[key] = value;
            }
        }

        /// <summary>
        /// 将输入字符串解析为int，如果解析成功，返回解析值，否则返回defaultValue(默认值为-1)。
        /// </summary>
        /// <param name="key">输入字符串</param>
        /// <param name="defaultValue">默认值，解析失败时返回此值。</param>
        /// <returns></returns>
        public int GetInt(string key, int defaultValue = -1)
        {
            var value = this[key];
            return value != null && int.TryParse(value, out int v) ? v : defaultValue;
        }

        /// <summary>
        /// 如果键存在返回对应的值，否则返回defaultValue(默认值为null)。
        /// </summary>
        /// <param name="key">输入字符串</param>
        /// <param name="defaultValue">默认值，解析失败时返回此值。</param>
        /// <returns></returns>
        public string GetString(string key, string defaultValue = null)
        {
            return this[key] ?? defaultValue;
        }

        /// <summary>
        /// 将输入字符串解析为float，如果解析成功，返回解析值，否则返回defaultValue(默认值为-1)。
        /// </summary>
        /// <param name="key">输入字符串</param>
        /// <param name="defaultValue">默认值，解析失败时返回此值。</param>
        /// <returns></returns>
        public float GetFloat(string key, float defaultValue = -1)
        {
            var value = this[key];
            return value != null && float.TryParse(value, out float v) ? v : defaultValue;
        }

        /// <summary>
        /// 将输入字符串解析为double，如果解析成功，返回解析值，否则返回defaultValue(默认值为-1)。
        /// </summary>
        /// <param name="key">输入字符串</param>
        /// <param name="defaultValue">默认值，解析失败时返回此值。</param>
        /// <returns></returns>
        public double GetDouble(string key, double defaultValue = -1)
        {
            var value = this[key];
            return value != null && double.TryParse(value, out double v) ? v : defaultValue;
        }


        /// <summary>
        /// 将输入字符串解析为 boolean，如果解析成功，返回解析值，否则返回defaultValue(默认值为false)。
        /// 为了保证使用更方便，结果为 true 时不区别大小写，或者结果为1也可以。
        /// </summary>
        /// <param name="key">输入字符串</param>
        /// <param name="defaultValue">默认值，解析失败时返回此值。</param>
        /// <returns></returns>
        public bool GetBoolean(string key, bool defaultValue = false)
        {
            var value = this[key];
            return value != null && (value.ToLower() == "true" || value == "1") ? true : defaultValue;
        }

        /// <summary>
        /// 将输入字符串解析为 DateTime，如果解析成功，返回解析值，否则返回 DateTime.MinValue。
        /// </summary>
        /// <param name="key">输入字符串</param>
        /// <returns></returns>
        public DateTime GetDateTime(string key)
        {
            var v = dict[key];
            DateTime dt = DateTime.MinValue;
            try { dt = DateTime.Parse(v); } catch { }
            return dt;
        }


        /// <summary>
        /// 将目标对象object以键key赋值给字典。
        /// </summary>
        /// <param name="key">键值名。</param>
        /// <param name="value">数据对象。</param>
        public void SetValue(string key, object value)
        {
            if (value != null)
                this[key] = value + "";
        }

        #endregion


        /// <summary>
        /// 字典中是否包括指定的值。
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public bool ContainsKey(string key) => key != null && dict.ContainsKey(key);


        /// <summary>
        /// 从文件中加载数据内容，加载失败则字典内容为空，但不是null。
        /// </summary>
        /// <param name="configfile">默认为空，使用 ConfigFilepath 进行加载，如果不存在，则为空字典，但不是null。</param>
        public void Load(string configfile = null)
        {
            if (File.Exists(configfile))
            {
                ConfigFilepath = configfile;
                dict = LoadDictionary(configfile);
            }
        }

        /// <summary>
        /// 将文件
        /// </summary>
        /// <param name="filepath"></param>
        public void Save(string filepath)
        {
            StringBuilder sb = new StringBuilder();
            foreach (var key in dict.Keys)
                sb.AppendLine($"{key}={dict[key]}");
            File.WriteAllText(filepath, sb.ToString(), Encoding.UTF8);
        }

        /// <summary>
        /// 根据当前类，生成用于访问的CS类（测试用）。
        /// </summary>
        /// <returns></returns>
        public string ToCSharpString(string className = "cf")
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine($"public class {className}");
            sb.AppendLine("{");

            foreach (var key in dict.Keys)
                sb.AppendLine($"    public static string {key} = \"{key}\";");

            sb.AppendLine("}");
            return sb.ToString();
        }

        /// <summary>
        /// 从文件中加载字典，加载失败则返回空字典。
        /// </summary>
        /// <param name="configFile">待加载文件。</param>
        /// <param name="separator">分隔符，默认为等于号。</param>
        /// <param name="encoding">字符编码，默认为UTF-8。</param>
        /// <returns>返回键值均为字符串的字典。</returns>
        public static Dictionary<string, string> LoadDictionary(string configFile, char separator = '=', string encoding = "UTF-8")
        {
            // 文件如果不存在则返回null。
            var dicfile = Path.GetFullPath(configFile);
            if (!File.Exists(dicfile))
                return new Dictionary<string, string>();

            Dictionary<string, string> dic = new Dictionary<string, string>();
            try
            {
                foreach (var row in File.ReadAllLines(dicfile))
                {
                    var rowdata = row;

                    // 如果是注释（以#或//开头的行）则跳过
                    if (rowdata.StartsWith("#") || rowdata.StartsWith("//"))
                        continue;

                    // 定位
                    int pos = row.IndexOf(separator);
                    if (pos < 0 || pos >= row.Length)
                        continue;

                    // 解析出Key和Value进行添加
                    string key = row.Substring(0, pos).Trim();
                    string value = row.Substring(pos + 1);

                    // 只添加第一次读取的值。
                    if (!dic.ContainsKey(key))
                        dic.Add(key, value);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                return new Dictionary<string, string>();
            }
            return dic;
        }
    }
}

